/**
 * ReportGear(2011)
 */
package com.reportgear.report.view.grid;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolTip;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;

import com.reportgear.core.swing.pane.base.JBaseDialog;
import com.reportgear.report.core.model.util.BaseUtils;
import com.reportgear.report.model.auxiliary.ColumnPropList;
import com.reportgear.report.model.auxiliary.ColumnRow;
import com.reportgear.report.model.auxiliary.ColumnType;
import com.reportgear.report.view.JWorksheet;
import com.reportgear.report.view.cursor.CursorUtils;
import com.reportgear.report.view.grid.pane.NumberInputPane;
import com.reportgear.report.view.grid.selection.GridSelection;
import com.reportgear.report.view.grid.selection.Selection;
import com.reportgear.report.view.grid.support.DefaultGridColumnCRRenderer;
import com.reportgear.report.view.grid.support.GridCRRenderer;
import com.reportgear.report.view.grid.util.GraphHelper;
import com.reportgear.report.view.util.WorksheetUtils;

/**
 * Grid列头
 * <p>
 * 表格视图列头，对视图处理视图列,支持调整视图列的宽度,插入列等操作
 * 
 * @version 1.0 2011-3-14
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 * 
 */
public class GridColumnHeader extends BaseGridComponent {

	/**
	 * 鼠标动作处理
	 */
	private class GridColumnHeaderMouseHandler extends MouseInputAdapter {
		private JToolTip columnTip = null;
		private int dragColumnIndex = 0;
		private DragType dragType = DragType.DRAG_NONE;
		private int endMultiSelectIndex = 0;
		private boolean isDragPermited = false;
		private boolean isMulitColumnsSelectDragPermited = false;
		private int startMultiSelectIndex = 0;
		private JWindow tipWindow = null;

		public GridColumnHeaderMouseHandler() {
			this.isMulitColumnsSelectDragPermited = false;
			this.startMultiSelectIndex = 0;
			this.endMultiSelectIndex = 0;
			this.isDragPermited = false;
			this.dragColumnIndex = 0;
			this.columnTip = null;
			this.tipWindow = null;
		}

		private String createToolTipString(double d, double d1) {
			return "宽度: " + d + "/" + d1 + "像素";
		}

		private String createToolTipString(int i) {
			return i + "C";
		}

		/**
		 * 选择列
		 * 
		 * @param column
		 *            列
		 */
		private void doSelectColumn(int column) {
			final GridSelection gridSelection = worksheet.getGridSelection();
			gridSelection.setType(GridSelection.Type.CELL);
			this.startMultiSelectIndex = this.endMultiSelectIndex = column;

			gridSelection.setEditBounds(column, 0, 1, 1);
			gridSelection.setOnlySelection(column, 0, 1, worksheet.getRowCount());

			worksheet.refresh();
			worksheet.fireGridSelectionChanged();
		}

		private void hideToolTip() {
			if (this.tipWindow != null) {
				this.tipWindow.setVisible(false);
			}
		}

		private boolean isOnNormalSeparatorLine(double value, double refValue) {
			return Math.abs(value - refValue) < 2D;
		}

		@Override
		public void mouseDragged(MouseEvent evt) {
			if (!isEnabled()) {
				return;
			}

			if (this.dragType == DragType.DRAG_NONE
					|| SwingUtilities.isRightMouseButton(evt)) {
				return;
			}

			worksheet.stopEditing();

			GridSelection gridSelection = worksheet.getGridSelection();
			gridSelection.setType(GridSelection.Type.CELL);
			int xPos = evt.getX();
			int yPos = evt.getY();

			if (this.dragType == DragType.DRAG_SELECTED_COLUMNS) {// 多列选择
				if (!this.isMulitColumnsSelectDragPermited) {
					return;
				}

				final ColumnRow columnrow = WorksheetUtils.getColumnRowByXY(worksheet, xPos, yPos);

				this.endMultiSelectIndex = columnrow.getColumn();
				final int colSpan = Math.abs(this.endMultiSelectIndex - this.startMultiSelectIndex) + 1;
				final int startIndex = Math.min(this.startMultiSelectIndex, this.endMultiSelectIndex);

				gridSelection.setEditBounds(startIndex, 0, 1, 1);
				gridSelection.setOnlySelection(startIndex, 0, colSpan, worksheet.getRowCount());

				this.setToolTipText2(this.createToolTipString(colSpan));

				worksheet.fireGridSelectionChanged();
			} else {// 单行选择
				if (!this.isDragPermited) {
					return;
				}

				final ColumnPropList cpl = reportModel.getColumnPropList();

				final int w = xPos - cpl.getRangeValue(worksheet.getHoriBeginValue(), this.dragColumnIndex);
				cpl.setValue(this.dragColumnIndex, w);

				this.setToolTipText2(this.createToolTipString(cpl.getValue(this.dragColumnIndex), cpl.getRangeValue(0,
						this.dragColumnIndex + 1)));
			}

			worksheet.refresh();
		}

		@Override
		public void mouseMoved(MouseEvent evt) {
			if (!isEnabled()) {
				return;
			}

			setCursor(CursorUtils.CURSOR_H_SELECT);
			double xPos = evt.getX();
			ColumnPropList cpl = reportModel.getColumnPropList();

			double tmpWidth = 0.0D;
			double colWidth = 0.0D;
			double tmpTotalWidth = 0.0D;

			double width = getSize().getWidth();
			int pointer = worksheet.getHoriBeginValue();

			while (tmpTotalWidth < width) {
				tmpWidth += colWidth;
				colWidth = cpl.getValue(pointer);

				if (colWidth <= 0.0D) {
					tmpTotalWidth = tmpWidth + 1.0D;
				} else {
					tmpTotalWidth = tmpWidth + colWidth;
				}

				if (this.isOnNormalSeparatorLine(xPos, tmpTotalWidth)) {
					setCursor(CursorUtils.CURSOR_H_MOVE);
					break;
				}
				pointer++;
			}

		}

		@Override
		public void mousePressed(MouseEvent evt) {

			if (!isEnabled()) {
				return;
			}

			worksheet.stopEditing();

			int xPos = evt.getX();
			int yPos = evt.getY();
			boolean isRightButton = SwingUtilities.isRightMouseButton(evt);
			ColumnPropList cpl = reportModel.getColumnPropList();

			double tmpWidth = 0.0D;
			double columnWidth = 0.0D;
			double tmpTotalWidth = 0.0D;
			double width = getSize().getWidth();

			int pointer = worksheet.getHoriBeginValue();
			while (tmpTotalWidth < width) {
				tmpWidth += columnWidth;
				columnWidth = cpl.getValue(pointer);

				if (columnWidth <= 0.0D) {
					tmpTotalWidth = tmpWidth + 1.0D;
				} else {
					tmpTotalWidth = tmpWidth + columnWidth;
				}

				if (this.isOnNormalSeparatorLine(xPos, tmpTotalWidth)) {
					this.dragType = DragType.DRAG_WIDTH;
					this.isDragPermited = true;
					this.dragColumnIndex = pointer;
					final MouseEvent event = evt;
					final ColumnPropList pl = cpl;
					SwingUtilities.invokeLater(new Runnable() {

						public void run() {
							showToolTip(event, createToolTipString(pl.getValue(dragColumnIndex), pl.getRangeValue(0,
									dragColumnIndex + 1)));
						}
					});

					break;
				}

				if (xPos > tmpWidth && xPos <= tmpTotalWidth) {
					this.dragType = DragType.DRAG_SELECTED_COLUMNS;
					this.isMulitColumnsSelectDragPermited = true;
					this.startMultiSelectIndex = pointer;
					this.showToolTip(evt, this.createToolTipString(this.startMultiSelectIndex + 1));
					break;
				}

				pointer++;
			}

			if (evt.getClickCount() > 1) {
				if (popupMenu == null) {
					popupMenu = new GridColumnPopupMenu();
				}
				this.hideToolTip();
			}
			if (isRightButton) {
				if (popupMenu == null) {
					popupMenu = new GridColumnPopupMenu();
				}
				this.hideToolTip();
				popupMenu.show(GridColumnHeader.this, xPos, yPos);
			} else if (this.dragType == DragType.DRAG_SELECTED_COLUMNS) {
				worksheet.getGridSelection().setType(GridSelection.Type.CELL);

				ColumnRow columnRow = WorksheetUtils.getColumnRowByXY(worksheet, xPos, yPos);
				this.doSelectColumn(columnRow.getColumn());
			}

			worksheet.refresh();
		}

		@Override
		public void mouseReleased(MouseEvent evt) {
			if (!isEnabled()) {
				return;
			}

			if (this.dragType == DragType.DRAG_NONE
					|| SwingUtilities.isRightMouseButton(evt)) {
				return;
			}

			worksheet.stopEditing();
			this.hideToolTip();
			GridSelection gridSelection = worksheet.getGridSelection();
			Selection rect = gridSelection.getLastCellSelection();
			ColumnPropList cpl = reportModel.getColumnPropList();

			if (this.dragType == DragType.DRAG_WIDTH) {
				int col = rect.getColumn();
				int w = rect.getColumSpan();

				if ((this.dragColumnIndex - col < w) && (this.dragColumnIndex >= col)) {
					for (int i = 0; i < w; i++) {
						cpl.setValue(col + i, cpl.getValue(this.dragColumnIndex));
					}
				}
				worksheet.fireReportDataChanged();
			}

			this.isMulitColumnsSelectDragPermited = false;
			this.isDragPermited = false;
			this.dragType = DragType.DRAG_NONE;
			this.dragColumnIndex = 0;

			worksheet.repaint();
		}

		private void setToolTipText2(String s) {
			if (this.columnTip == null) {
				return;
			} else {
				this.columnTip.setTipText(s);
				this.columnTip.setSize(this.columnTip.getPreferredSize());
				this.tipWindow.pack();
				this.tipWindow.repaint();
				return;
			}
		}

		private void showToolTip(MouseEvent mouseevent, String s) {
			if (this.tipWindow == null) {
				this.tipWindow = new JWindow();
				this.columnTip = createToolTip();
				this.tipWindow.getContentPane().add(this.columnTip, "Center");
			}
			this.columnTip.setTipText(s);
			Point point = new Point(mouseevent.getX(), 0);
			SwingUtilities.convertPointToScreen(point, GridColumnHeader.this);
			Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
			Dimension dimension1 = this.columnTip.getPreferredSize();
			int i = Math.max(0, Math.min(point.x - dimension1.width / 2, dimension.width - dimension1.width));
			int j = point.y - dimension1.height - 2;
			this.tipWindow.setLocation(i, j);
			this.tipWindow.pack();
			this.tipWindow.setVisible(true);
		}
	}

	private class GridColumnPopupMenu extends JPopupMenu {
		public GridColumnPopupMenu() {
			this.add(new JMenuItem(new AddColumnAction()));
			this.add(new JMenuItem(new DeleteColumnAction()));
			this.addSeparator();
			this.add(new JMenuItem(new SetColumnWidthAction()));
			this.addSeparator();
			this.add(new JMenuItem(new SetRepeatColumnHeaderAction()));
			this.add(new JMenuItem(new SetRepeatColumnFooterAction()));
			this.add(new JMenuItem(new CancelRepeatRowAction()));
		}

		private class AddColumnAction extends AbstractAction {

			public AddColumnAction() {
				super("插入列");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				worksheet.getReportModel().addColumn(selection.getColumn());
				worksheet.refresh();
			}
		}

		/**
		 * 删除行
		 */
		private class DeleteColumnAction extends AbstractAction {

			public DeleteColumnAction() {
				super("删除列");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int col = selection.getColumn();
				int colCount = selection.getColumSpan();

				for (int i = colCount - 1; i >= 0; i--) {
					reportModel.removeColumn(col + i);
				}

				worksheet.refresh();
			}
		}

		private class SetColumnWidthAction extends AbstractAction {

			public SetColumnWidthAction() {
				super("设置列宽");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int col = selection.getColumn();
				int colCount = selection.getColumSpan();

				NumberInputPane inputPane = new NumberInputPane();
				inputPane.setTitle("宽度");
				inputPane.populate(reportModel.getColumnWidth(col));

				if (inputPane.showDialog(null) == JBaseDialog.RESULT_OK) {
					int value = (int) inputPane.update();
					for (int i = 0; i < colCount; i++) {
						reportModel.setColumnWidth(col + i, value);
					}

					worksheet.refresh();
				}
			}
		}

		private class SetRepeatColumnHeaderAction extends AbstractAction {

			public SetRepeatColumnHeaderAction() {
				super("设置重复标题列");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int col = selection.getColumn();
				int colCount = selection.getColumSpan();

				for (int i = 0; i < colCount; i++) {
					int index = col + i;

					ColumnType type = reportModel.getColumnType(index);

					reportModel.setColumnType(index, ColumnType.HEADER);
				}

				worksheet.refresh();
			}
		}

		private class SetRepeatColumnFooterAction extends AbstractAction {

			public SetRepeatColumnFooterAction() {
				super("设置重复结尾列");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int col = selection.getColumn();
				int colCount = selection.getColumSpan();

				for (int i = 0; i < colCount; i++) {
					int index = col + i;

					ColumnType type = reportModel.getColumnType(index);

					reportModel.setColumnType(index, ColumnType.FOOTER);
				}

				worksheet.refresh();
			}
		}

		private class CancelRepeatRowAction extends AbstractAction {

			public CancelRepeatRowAction() {
				super("取消重复行列");
			}

			public void actionPerformed(ActionEvent e) {
				Selection selection = worksheet.getLastCellSelection();

				int col = selection.getColumn();
				int colCount = selection.getColumSpan();

				for (int i = 0; i < colCount; i++) {
					int index = col + i;

					reportModel.setColumnType(index, ColumnType.NORMAL);
				}

				worksheet.refresh();
			}
		}
	}

	/**
	 * UID
	 */
	private static final long serialVersionUID = -7201198446814059531L;

	// 单元格Render
	private GridCRRenderer gridCRRender;
	// 弹出菜单
	private GridColumnPopupMenu popupMenu;
	// 选中背景颜色
	private Color selectedBackground = new Color(253, 216, 153);
	// 选中前景颜色
	private Color selectedForeground = Color.BLACK;

	// 分隔线颜色
	private Color separatorLineColor = new Color(172, 168, 153);

	// 临时矩形对象
	private Rectangle2D tmpRectangle2D = new Rectangle2D.Double(0, 0, 0, 0);

	public GridColumnHeader(JWorksheet workSheet) {
		super(workSheet);

		final GridColumnHeaderMouseHandler mouseHandler = new GridColumnHeaderMouseHandler();
		this.addMouseListener(mouseHandler);
		this.addMouseMotionListener(mouseHandler);
		this.gridCRRender = new DefaultGridColumnCRRenderer(this.reportModel);
		this.getInputMap().clear();
		this.getActionMap().clear();
		this.setFocusable(false);
		this.setOpaque(true);
	}

	/**
	 * 获取标题绘制对象
	 * 
	 * @return 标题绘制对象
	 */
	public GridCRRenderer getGridCRRender() {
		return this.gridCRRender;
	}

	@Override
	public Dimension getPreferredSize() {
		if (this.worksheet.isColumnHeaderVisible()) {
			return new Dimension(super.getPreferredSize().width, this.getFontMetrics(this.getFont()).getHeight() + 4);
		} else {
			return new Dimension(0, 0);
		}
	}

	/**
	 * 设置选择背景色
	 * 
	 * @return 选择背景色
	 */
	public Color getSelectedBackground() {
		return this.selectedBackground;
	}

	/**
	 * 获取选择前景色
	 * 
	 * @return 选择前景色
	 */
	public Color getSelectedForeground() {
		return this.selectedForeground;
	}

	/**
	 * 获取分割线颜色
	 * 
	 * @return 分割线颜色
	 */
	public Color getSeparatorLineColor() {
		return this.separatorLineColor;
	}

	@Override
	public void paintComponent(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		Dimension dim = this.getSize();

		// 绘制背景
		this.tmpRectangle2D.setRect(0.0D, 0.0D, dim.getWidth(), dim.getHeight());
		g2.setPaint(this.getBackground());
		GraphHelper.fill(g2, this.tmpRectangle2D);

		FontRenderContext frContext = g2.getFontRenderContext();
		LineMetrics lineMetrics = this.getFont().getLineMetrics("A", frContext);
		float ascent = lineMetrics.getAscent();

		// 绘制分隔线
		g2.setPaint(this.getSeparatorLineColor());
		GraphHelper.drawLine(g2, 0.0D, 0.0D, 0.0D, dim.getHeight());

		GridSelection gridSelection = this.worksheet.getGridSelection();

		ColumnPropList cpl = this.reportModel.getColumnPropList();
		double clientWidth = dim.getWidth();
		int i = this.worksheet.getHoriBeginValue();

		int colExtend = 0;
		double tmpWidth1 = 0.0D;
		double tmpWidth2 = 0.0D;
		double columnWidth = 0.0D;

		final List<Integer> selectedIndexList = BaseUtils.getGridSelectionColumnIndex(gridSelection);
		boolean tmpSelectStatus;
		while (tmpWidth2 < clientWidth) {
			tmpSelectStatus = false;
			tmpWidth1 += columnWidth;
			columnWidth = cpl.getValue(i);

			if (columnWidth <= 0.0D) {
				tmpWidth2 = tmpWidth1 + 1.0D;
			} else {
				tmpWidth2 = tmpWidth1 + columnWidth;
			}

			if (selectedIndexList.contains(i)) {
				g2.setColor(this.getSelectedBackground());
				GraphHelper.fill(g2, new Rectangle2D.Double(tmpWidth1 + 1.0D, 0.0D, columnWidth - 1.0D, dim.height));
				tmpSelectStatus = true;
			} else {
				tmpSelectStatus = false;
			}

			g2.setColor(this.getSeparatorLineColor());
			GraphHelper.drawLine(g2, tmpWidth2, 0.0D, tmpWidth2, dim.getHeight());
			final Object render = this.getGridCRRender().getDisplayInfo(i);

			if ((render instanceof String) || (render instanceof Number)) {
				final String str = render.toString() + cpl.getTypeStringShort(i);
				final double strWidth = this.getFont().getStringBounds(str, frContext).getWidth();

				if (tmpSelectStatus) {
					g2.setPaint(this.getSelectedForeground());
				} else if (this.isEnabled()) {
					g2.setPaint(this.getForeground());
				} else {
					g2.setPaint(UIManager.getColor("controlShadow"));
				}
				GraphHelper.drawString(g2, str, tmpWidth1 + (columnWidth - strWidth) / 2.0D, ascent + 2.0F + 1.0F);
			}

			i++;
			colExtend++;
		}
		// 设置视图显示列
		this.worksheet.setHoriValueExtend(colExtend);

		g2.setColor(this.getSeparatorLineColor());
		GraphHelper.drawLine(g2, 0.0D, 0.0D, clientWidth, 0.0D);
	}

	/**
	 * 设置标题绘制对象
	 * 
	 * @param render
	 *            绘制对象
	 */
	public void setGridCRRender(GridCRRenderer render) {
		this.gridCRRender = render;
	}

	/**
	 * 设置选择背景色
	 * 
	 * @param newColor
	 *            新颜色
	 */
	public void setSelectedBackground(Color newColor) {
		this.selectedBackground = newColor;
		this.repaint();
	}

	/**
	 * 设置选择前景色
	 * 
	 * @param newColor
	 *            新颜色
	 */
	public void setSelectedForeground(Color newColor) {
		this.selectedForeground = newColor;
		this.repaint();
	}

	/**
	 * 设置分隔线颜色
	 * 
	 * @param newColor
	 *            新颜色
	 */
	public void setSeparatorLineColor(Color newColor) {
		this.separatorLineColor = newColor;
		this.repaint();
	}
}